<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h3 data-lake-id="kIIiW" id="kIIiW"><span data-lake-id="u9b1732aa" id="u9b1732aa">背景</span></h3>
  <p data-lake-id="u635baf7b" id="u635baf7b"><br></p>
  <p data-lake-id="ua44f7591" id="ua44f7591"><span data-lake-id="u64175a6d" id="u64175a6d">项目中，需要实现一个通过短信验证码进行注册的场景，为了这个过程中5分钟只能发放一次验证码，并且验证码发放后可以验证一次的功能，使用了Redis进行存储。</span></p>
  <p data-lake-id="ufff28dc4" id="ufff28dc4"><span data-lake-id="u1f4a5e83" id="u1f4a5e83">​</span><br></p>
  <p data-lake-id="u45e2cbe5" id="u45e2cbe5"><span data-lake-id="u5e941160" id="u5e941160">主要是为了避免盗刷，避免资金浪费。</span></p>
  <p data-lake-id="ucbe4d435" id="ucbe4d435"><span data-lake-id="uc3c3405a" id="uc3c3405a">​</span><br></p>
  <h3 data-lake-id="Jz35o" id="Jz35o"><span data-lake-id="u641e0b24" id="u641e0b24">技术选型</span></h3>
  <p data-lake-id="ue84caf78" id="ue84caf78"><br></p>
  <h4 data-lake-id="t0n9D" id="t0n9D"><span data-lake-id="udfc7fbb7" id="udfc7fbb7">验证码生成</span></h4>
  <p data-lake-id="u5fecc82b" id="u5fecc82b"><br></p>
  <p data-lake-id="udf4da272" id="udf4da272"><span data-lake-id="u1f4f916e" id="u1f4f916e">验证码的生成，代码中自己可以简单实现一个验证码，发送四位的字母+数字组合即可，这个场景对于全局唯一性要求并不高。也可以用一些开源的验证码生成的框架。</span></p>
  <p data-lake-id="ucec971bf" id="ucec971bf"><br></p>
  <h4 data-lake-id="N1CQX" id="N1CQX"><span data-lake-id="u844364f0" id="u844364f0">短信发送</span></h4>
  <p data-lake-id="u360ef0d4" id="u360ef0d4"><span data-lake-id="u8573c38e" id="u8573c38e">短信验证码的发放，在阿里云上有很多服务，可以实现短信发送。</span></p>
  <p data-lake-id="u04c69d9e" id="u04c69d9e"><span data-lake-id="ud6c94eea" id="ud6c94eea">​</span><br></p>
  <p data-lake-id="u1015c713" id="u1015c713"><a href="https://www.aliyun.com/search?spm=5176.21213303.J_6704733920.6.34b853c98er6CO&amp;k=%E7%9F%AD%E4%BF%A1%E9%AA%8C%E8%AF%81%E7%A0%81&amp;scene=market&amp;page=1" target="_blank" data-lake-id="u1c4b2d43" id="u1c4b2d43"><span data-lake-id="uf64fa418" id="uf64fa418">https://www.aliyun.com/search?spm=5176.21213303.J_6704733920.6.34b853c98er6CO&amp;k=%E7%9F%AD%E4%BF%A1%E9%AA%8C%E8%AF%81%E7%A0%81&amp;scene=market&amp;page=1</span></a></p>
  <p data-lake-id="u81792588" id="u81792588"><br></p>
  <p data-lake-id="uf623accd" id="uf623accd"><br></p>
  <p data-lake-id="u196a8447" id="u196a8447"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1693112003377-d243f86d-fc21-4b34-a498-f5de2164bead.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_32%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u0ce2b0c2" id="u0ce2b0c2"><br></p>
  <p data-lake-id="u81bb87e9" id="u81bb87e9"><span data-lake-id="u50b179d8" id="u50b179d8">对接都很简单，只需要通过HTTP接口调用即可实现短信发送。</span></p>
  <p data-lake-id="u0bfde88c" id="u0bfde88c"><span data-lake-id="u10e6a266" id="u10e6a266">​</span><br></p>
  <h4 data-lake-id="wGqGW" id="wGqGW"><span data-lake-id="ufb7a0ad2" id="ufb7a0ad2">验证码存储&amp;验证</span></h4>
  <p data-lake-id="u7cab5dec" id="u7cab5dec"><br></p>
  <p data-lake-id="u62ee9744" id="u62ee9744"><span data-lake-id="ucc0136ca" id="ucc0136ca">Redis是一个分布式的缓存，可以把验证码信息临时放到Redis中，借助redis的超时自动删除功能，可以实现5分钟内才能进行验证码的校验的功能。</span></p>
  <p data-lake-id="ue6ea9bd7" id="ue6ea9bd7"><span data-lake-id="u977adfb9" id="u977adfb9">​</span><br></p>
  <p data-lake-id="uab7580b0" id="uab7580b0"><span data-lake-id="u5fa9067e" id="u5fa9067e">​</span><br></p>
  <h4 data-lake-id="iewZW" id="iewZW"><span data-lake-id="uc6bbd538" id="uc6bbd538">防止重复发送</span></h4>
  <p data-lake-id="u53795c31" id="u53795c31"><br></p>
  <p data-lake-id="ub0900603" id="ub0900603"><span data-lake-id="u6eddc05e" id="u6eddc05e">为了防止重复发送，可以在发送验证码的地方，增加一个分布式锁，保证一条消息只会被发送一次。</span></p>
  <p data-lake-id="u503628d6" id="u503628d6"><br></p>
  <h3 data-lake-id="dlORN" id="dlORN"><span data-lake-id="u40f52f43" id="u40f52f43">你做了什么</span></h3>
  <p data-lake-id="u41b2e077" id="u41b2e077"><br></p>
  <pre lang="java"><code>
  public void sendSmsVerificationCode(@NotBlank String mobile, @NotBlank String type) {
      String smsSended = redisTemplate.opsForValue().get(getLimitKey(mobile, type));
      if (StrUtil.isNotBlank(smsSended)) {
          throw new BizException("请勿频繁发送验证码");
      }

    	//记录手机号发送过验证码，记录1分钟
      redisTemplate.opsForValue().set(getLimitKey(mobile, type), "1", 60, TimeUnit.SECONDS);
    	//使用hutool的工具生成四位随机验证码
      String verificationCode = RandomUtil.randomStringUpper(4);
    	//把验证码存在Redis中
      redisTemplate.opsForValue().set(type + mobile, verificationCode, 60 * 5, TimeUnit.SECONDS);

    	//构建一条发送记录
      SmsSendRecord smsSendRecord = new SmsSendRecord(mobile, type, verificationCode);
    	//落库保存
      smsSendRecordRepo.save(smsSendRecord);

    	//发送短信验证码
      smsService.sendSms(smsSendRecord)；
  }
</code></pre>
  <p data-lake-id="u3e49499d" id="u3e49499d"><br></p>
  <pre lang="java"><code>
@Service
public class SmsService{
  @Autowired
  RedissonClient redisson;
  
  public void sendSms(SmsSendRecord smsSendRecord) {
  	//加分布式锁
		RLock lock = redisson.getLock("sendSms" + smsSendRecord.getId());
    try {
        lock.lock();
        //短信发送逻辑
    } finally {
      	//解锁
        lock.unlock();
    }
  }
}

</code></pre>
  <p data-lake-id="u686fd0d8" id="u686fd0d8"><span data-lake-id="uc27821a2" id="uc27821a2">​</span><br></p>
  <p data-lake-id="u0d755d0c" id="u0d755d0c"><span data-lake-id="uc4c4e86c" id="uc4c4e86c">以上，就是一段简单的，短信验证码发送的功能</span></p>
  <p data-lake-id="ufe88c097" id="ufe88c097"><span data-lake-id="uca500052" id="uca500052">​</span><br></p>
  <p data-lake-id="ud952c3e5" id="ud952c3e5"><span data-lake-id="u48ffb375" id="u48ffb375">以下是简单的验证码验证的逻辑</span></p>
  <pre lang="java"><code>

	public Boolean verifySmsCode(LoginParam param) {
  	//从Redis中取出验证码
		String verificationCode = redisTemplate.opsForValue().get(param.getType() + param.getMobile());
    	if(verificationCode ==null){
      	return false;
      }
			if (param.getVerificationCode().equals(verificationCode)) {
      	redisTemplate.delete(param.getType() + param.getMobile());
      	return true;
			}

    	return false;
		}
	}
</code></pre>
  <p data-lake-id="u22984026" id="u22984026"><br></p>
  <h3 data-lake-id="EgNab" id="EgNab"><span data-lake-id="u5d1b21cd" id="u5d1b21cd">学习资料</span></h3>
  <p data-lake-id="ue4f18427" id="ue4f18427"><span data-lake-id="u5b9b26d2" id="u5b9b26d2" style="color: rgb(38, 38, 38)"><br><br></span></p>
  <p data-lake-id="u33957964" id="u33957964"><span data-lake-id="u311473c7" id="u311473c7">​</span><br></p>
  <p data-lake-id="u6ac3b7aa" id="u6ac3b7aa"><br></p>
  <p data-lake-id="u3d15417e" id="u3d15417e"><br></p>
  <p data-lake-id="u3f47e9bd" id="u3f47e9bd"><br></p>
  <p data-lake-id="u4b67c2ac" id="u4b67c2ac"><br></p>
 </body>
</html>